#include "ByteCodeWriter.h"
#include "ByteCodeFile.h"

uint32_t CBytecodeWriter::GetByteCodeSize() const
{
    return ByteCode->ByteCode.Num();
}

void CBytecodeWriter::Finish()
{
    TVector<uint8_t> ByteCodeTemp = std::move(ByteCode->ByteCode);

    ByteCode->ByteCode.Reserve(100000);

    //insert file table after string table
    uint32_t FileTableSize = SourceCodeFileTable.Num();
    *this << FileTableSize;
    for (const CByteCodeFile::SSourceCodeFile& File : SourceCodeFileTable)
    {
        *this << File.FilePath.ToString();
        *this << File.LineCount;
    }

    TVector<uint8_t> FileTableTemp = std::move(ByteCode->ByteCode);

    ByteCode->ByteCode.Reserve(100000);

    //add string table to the beginning of the byte code
    uint32_t StringTableSize = StringTable.Num();
    *this << StringTableSize;
    for (const CString& String : StringTable)
    {
        *this <<(uint32_t)String.GetLength();
        for(uint64_t i=0;i<String.GetLength();i++)
        {
            *this << (uint32_t) (*(String.GetData()+i));
        }
    }

    ByteCode->ByteCode.Append(FileTableTemp);
    ByteCode->ByteCode.Append(ByteCodeTemp);

}

CBytecodeWriter& CBytecodeWriter::operator<<(EByteCodeInstructions Instruction)
{
    uint16_t InstructionValue = static_cast<uint16_t>(Instruction);
    *this << InstructionValue;
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(EByteCodeSectionType SectionType)
{
    uint8_t SectionTypeValue = static_cast<uint8_t>(SectionType);
    *this << SectionTypeValue;
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(uint32_t Value)
{
    //small endian
    ByteCode->ByteCode.Add(Value & 0xFF);
    ByteCode->ByteCode.Add((Value >> 8) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 16) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 24) & 0xFF);
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(uint64_t Value)
{
    //small endian
    ByteCode->ByteCode.Add(Value & 0xFF);
    ByteCode->ByteCode.Add((Value >> 8) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 16) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 24) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 32) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 40) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 48) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 56) & 0xFF);
    return *this;

}

CBytecodeWriter& CBytecodeWriter::operator<<(STypeID Value)
{
    *this<<Value.GetTypeFullName();
    *this<<(uint8_t)Value.IsScriptType();
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(SFunctionID Value)
{
    *this<<Value.TypeID;
    *this<<Value.FuncitonIndex;
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(int32_t Value)
{
    //small endian
    ByteCode->ByteCode.Add(Value & 0xFF);
    ByteCode->ByteCode.Add((Value >> 8) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 16) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 24) & 0xFF);
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(uint16_t Value)
{
    //small endian
    ByteCode->ByteCode.Add(Value & 0xFF);
    ByteCode->ByteCode.Add((Value >> 8) & 0xFF);
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(uint8_t Value)
{
    ByteCode->ByteCode.Add(Value);
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(float Value)
{
    assert(sizeof(float) == 4);

    uint32_t ValueInt = *(uint32_t *)&Value;
    *this << ValueInt;

    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(double Value)
{
    assert(sizeof(double) == 8);

    uint64_t ValueInt = *(uint64_t *)&Value;
    *this << ValueInt;

    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(const CString &Value)
{
    auto Found= StringTable.Find(Value);
    if(Found.HasValue())
    {
        uint32_t Index =  Found.GetValue();
        *this << Index;
        return *this;
    }

    uint32_t Index =StringTable.Num();
    StringTable.Add(Value);
    *this << Index;
    return *this;
}

CBytecodeWriter &CBytecodeWriter::operator<<(const CConstantValue &Value)
{
    static_assert(std::is_same_v<decltype(Value.Type), EConstantValueType>);
    static_assert(std::is_same_v<uint8_t,std::underlying_type_t<EConstantValueType>>);
    *this <<(uint8_t)Value.Type;
    
    switch(Value.Type)
    {
        case EConstantValueType::Char:
            *this << Value.CharValue;
            break;
        case EConstantValueType::Bool:
            *this << Value.BoolValue;
            break;
        case EConstantValueType::Int8:
            *this << Value.Int8Value;
            break;
        case EConstantValueType::Int16:
            *this << Value.Int16Value;
            break;
        case EConstantValueType::Int32:
            *this << Value.Int32Value;
            break;
        case EConstantValueType::Int64:
            *this << Value.Int64Value;
            break;
        case EConstantValueType::UInt8:
            *this << Value.UInt8Value;
            break;
        case EConstantValueType::UInt16:
            *this << Value.UInt16Value;
            break;
        case EConstantValueType::UInt32:
            *this << Value.UInt32Value;
            break;
        case EConstantValueType::UInt64:
            *this << Value.UInt64Value;
            break;
        case EConstantValueType::Float:
            *this << Value.FloatValue;
            break;
        case EConstantValueType::Double:
            *this << Value.DoubleValue;
            break;
        case EConstantValueType::String:
            *this << Value.StringValue;
            break;
        default:
            assert(false&&"unimplemented");
            break;
    }

    return *this;

}

CBytecodeWriter& CBytecodeWriter::operator<<(const CByteCodeFile::SBreakpointInfo &BreakpointInfo)
{
    *this << BreakpointInfo.FileIndex;
    *this << BreakpointInfo.SourceCodeLineNumber;
    *this << BreakpointInfo.InstructionIndex;
    return *this;
}

// CBytecodeWriter& CBytecodeWriter::operator<<(const CString &Value)
// {
//    *this<< (uint32_t)Value.length();
//     for(auto &Char:Value)
//     {
//          *this<<(uint32_t)Char;
//     }

//     return *this;
// }
CBytecodeWriter& CBytecodeWriter::operator <<(const TVector<int64_t>& Value)
{
    *this << (uint32_t)Value.Num();
    for (auto &SubValue : Value)
    {
        *this << SubValue;
    }
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(const TVector<CString> &Value)
{
    *this << (uint32_t)Value.Num();
    for (auto &Str : Value)
    {
        *this << Str;
    }
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(const CByteCodeFile::STypeInfo &TypeInfo)
{
    *this << TypeInfo.TypeID;
    *this << (uint8_t)TypeInfo.OverrideGenericTypes.Num();
    for(auto & OverrideGenericType : TypeInfo.OverrideGenericTypes)
    {
        *this << OverrideGenericType;
    }
    return *this;
}

CBytecodeWriter &CBytecodeWriter::operator<<(const CByteCodeFile::SVariableInfo &VariableInfo)
{
    *this << VariableInfo.Type;
    *this << VariableInfo.Name;
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator <<(const TVector<CByteCodeFile::STypeInfo>& TypeInfos)
{
    *this << (uint32_t)TypeInfos.Num();
    for (auto &TypeInfo : TypeInfos)
    {
        *this << TypeInfo;
    }
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(EScriptAccessLevel AccessLevel)
{
    uint8_t AccessLevelValue = static_cast<uint8_t>(AccessLevel);
    *this << AccessLevelValue;
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(const CByteCodeFile::SMemberDecorator &Decorator)
{
    uint8_t DecoratorValue = Decorator.bIsConst;
    DecoratorValue |= Decorator.bIsStatic << 1;
    *this << DecoratorValue;
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(const CByteCodeFile::SFunctionCallInfo &FunctionCallInfo)
{
    *this << FunctionCallInfo.Class;
    *this << FunctionCallInfo.Function;
    return *this;
}

// void CBytecodeWriter::AddHead(const SHead &Head)
// {
//     *this << EByteCodeSectionType::Head;

//     *this << Head.TypeNumer;
// }

CBytecodeWriter& CBytecodeWriter::operator <<(const TVector<uint32_t>& Value)
{
    *this << (uint32_t)Value.Num();
    for (auto &SubValue : Value)
    {
        *this << SubValue;
    }
    return *this;
}

CBytecodeWriter &CBytecodeWriter::operator<<(bool Value)
{
    *this << (uint8_t)Value;
    return *this;
}

CBytecodeWriter &CBytecodeWriter::operator<<(char32_t Value)
{
    *this << (uint32_t)Value;
    return *this;
}

CBytecodeWriter &CBytecodeWriter::operator<<(int8_t Value)
{
    *this << (uint8_t)Value;
    return *this;
}

CBytecodeWriter &CBytecodeWriter::operator<<(int16_t Value)
{
    *this << (uint16_t)Value;
    return *this;
}

CBytecodeWriter& CBytecodeWriter::operator<<(int64_t Value)
{
    //small endian
    ByteCode->ByteCode.Add(Value & 0xFF);
    ByteCode->ByteCode.Add((Value >> 8) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 16) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 24) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 32) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 40) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 48) & 0xFF);
    ByteCode->ByteCode.Add((Value >> 56) & 0xFF);
    return *this;

}

uint32_t CBytecodeWriter::BeginNamespace(const CString &NamespaceName, const TVector<CString> &InParentNamespaceID)
{
    uint32_t Index = ByteCode->ByteCode.Num();
    uint32_t NamespaceContentLength =0; // name space length ,exclude the namespace instruction , include the name
    *this << EByteCodeSectionType::Namespace;
    *this << NamespaceContentLength;
    *this << NamespaceName;
    *this << InParentNamespaceID;
    return Index;
}

void CBytecodeWriter::EndNamespace(uint32_t NamespaceIndex)
{
    uint32_t NamespaceContentLength = ByteCode->ByteCode.Num() - NamespaceIndex - 5;
    ByteCode->ByteCode[NamespaceIndex + 1 +0] = NamespaceContentLength & 0xFF;
    ByteCode->ByteCode[NamespaceIndex + 1+ 1] = (NamespaceContentLength >> 8) & 0xFF;
    ByteCode->ByteCode[NamespaceIndex + 1+ 2] = (NamespaceContentLength >> 16) & 0xFF;
    ByteCode->ByteCode[NamespaceIndex + 1+ 3] = (NamespaceContentLength >> 24) & 0xFF;
}

// void CBytecodeWriter::AddInnerType(const CString &TypeName)
// {
//     StringTable.Add(TypeName);
// }

uint32_t CBytecodeWriter::BeginClass(const CByteCodeFile::SClassInfo &InClassInfo)
{
    uint32_t Index = ByteCode->ByteCode.Num();
    uint32_t ClassContentLength =0; // class length ,exclude the  instruction , include the name
    *this << EByteCodeSectionType::Class;
    *this << ClassContentLength;
    *this << InClassInfo.AccessLevel;
    *this << InClassInfo.Name;
    *this << InClassInfo.NameScopeID;

    uint8_t Byte = InClassInfo.IsAbstract;
    Byte |= (InClassInfo.bIsInnerType ? 1 << 1 : 0);
    Byte |= (InClassInfo.bIsStruct ? 1 << 2 : 0);
    Byte |= (InClassInfo.bIsEnum ? 1 << 3 : 0);
    Byte |= (InClassInfo.bIsDelegate ? 1 << 4 : 0);

    *this << Byte;
    *this << InClassInfo.TypeID;
    *this << InClassInfo.BaseTypeID;
    *this << InClassInfo.RawClassTypeID;

    {
        uint32_t GenericPlaceholderNum= InClassInfo.GenericPlaceholders.Num();
        *this << GenericPlaceholderNum;

        for(auto & GenericPlaceholder : InClassInfo.GenericPlaceholders)
        {
            *this << GenericPlaceholder.TypeID;
            *this << GenericPlaceholder.Name;
        }
    }

    if(InClassInfo.bIsEnum)
    {
        *this << InClassInfo.EnumNames;
        *this << InClassInfo.EnumValues;
    }

    if(InClassInfo.bIsDelegate)
    {
        *this << InClassInfo.DelegateFunctionType;
    }

    return Index;
}

void CBytecodeWriter::EndClass(uint32_t ClassIndex)
{
    uint32_t ClassContentLength = ByteCode->ByteCode.Num() - ClassIndex - 5;
    ByteCode->ByteCode[ClassIndex + 1 +0] = ClassContentLength & 0xFF;
    ByteCode->ByteCode[ClassIndex + 1+ 1] = (ClassContentLength >> 8) & 0xFF;
    ByteCode->ByteCode[ClassIndex + 1+ 2] = (ClassContentLength >> 16) & 0xFF;
    ByteCode->ByteCode[ClassIndex + 1+ 3] = (ClassContentLength >> 24) & 0xFF;
}

void CBytecodeWriter::AddGeneralizedClass(const CByteCodeFile::SGeneralizedClasseInfo & GeneralizedClass)
{
    *this<<EByteCodeSectionType::GeneralizedClass;

    *this << GeneralizedClass.TypeID;
    *this << GeneralizedClass.RawClassTypeID;
    *this << GeneralizedClass.BaseTypeID.HasValue();
    if(GeneralizedClass.BaseTypeID.HasValue())
    {
        *this << GeneralizedClass.BaseTypeID.GetValue();
    }
    *this << GeneralizedClass.Name;
    *this << GeneralizedClass.TemplateID;
    *this << (uint32_t)GeneralizedClass.ArgumentInfos.Num();
    for(auto & ArgumentTypeID : GeneralizedClass.ArgumentInfos)
    {
        *this << ArgumentTypeID;
    }
    
}

uint32_t CBytecodeWriter::BeginClassContent(STypeID ClassID)
{
    uint32_t Index = ByteCode->ByteCode.Num();
    *this << EByteCodeSectionType::ClassContent;
    uint32_t ClassContentLength =0; // class length ,exclude the  instruction , include the name
    *this << ClassContentLength;
    *this << ClassID;
    return Index;
}

void CBytecodeWriter::EndClassContent(uint32_t ClassContentIndex)
{
    uint32_t ClassContentLength = ByteCode->ByteCode.Num() - ClassContentIndex - 5;
    ByteCode->ByteCode[ClassContentIndex + 1 +0] = ClassContentLength & 0xFF;
    ByteCode->ByteCode[ClassContentIndex + 1+ 1] = (ClassContentLength >> 8) & 0xFF;
    ByteCode->ByteCode[ClassContentIndex + 1+ 2] = (ClassContentLength >> 16) & 0xFF;
    ByteCode->ByteCode[ClassContentIndex + 1+ 3] = (ClassContentLength >> 24) & 0xFF;

}

void CBytecodeWriter::AddFunctionType(const CByteCodeFile::SFunctionType &InFunctionType)
{
    *this << EByteCodeSectionType::FunctionType;
    *this << InFunctionType.TypeID;
    *this << InFunctionType.Name;
    *this << InFunctionType.ReturnType;
    *this << InFunctionType.ParameterTypes;
}

uint32_t CBytecodeWriter::BeginFunction()
{
    uint32_t Index = ByteCode->ByteCode.Num();

    // function length ,exclude the instruction and length , include the name, will be set when end function
    //uint32_t FunctionContentLength =0  ; 

    *this << EByteCodeSectionType::Function;
    //function length ,exclude the instruction and length , include the name, will be set when end function
    uint32_t FunctionContentLength =0  ; 
    *this << FunctionContentLength;
    // *this << FunctionInfo.AccessLevel;
    // *this << FunctionInfo.Decorator;
    // *this << FunctionInfo.ReturnType;
    // *this << FunctionInfo.Name;
    // *this << FunctionInfo.ParameterNames;
    // for(auto & Type : FunctionInfo.ParameterTypes)
    // {
    //     *this << Type;
    // }
    return Index;
}

// void CBytecodeWriter::EndFunction(uint32_t FunctionIndex)
// {
//     uint32_t FunctionContentLength = ByteCode->ByteCode.Num() - FunctionIndex - 6;
//     ByteCode[FunctionIndex + 2 +0] = FunctionContentLength & 0xFF;
//     ByteCode[FunctionIndex + 2+ 1] = (FunctionContentLength >> 8) & 0xFF;
//     ByteCode[FunctionIndex + 2+ 2] = (FunctionContentLength >> 16) & 0xFF;
//     ByteCode[FunctionIndex + 2+ 3] = (FunctionContentLength >> 24) & 0xFF;
// }

void CBytecodeWriter::EndFunction(uint32_t FunctionIndex, const CByteCodeFile::SFunctionInfo &FunctionInfo)
{
    //Content start
    uint32_t FunctionContentLengthPosition = FunctionIndex + 1  /*instruction*/ ;
    uint32_t ContentStart = FunctionContentLengthPosition +4 /*function Length*/;


    TVector<uint8_t> Instructions;
    if(ContentStart < ByteCode->ByteCode.Num())
    {   
        //not empty
        Instructions = ByteCode->ByteCode.Slice(ContentStart, ByteCode->ByteCode.Num() );
        ByteCode->ByteCode.RemoveAndAfter(ContentStart,false);
    }
    
    //function length ,exclude the instruction and length , include the name, will be set when end function
    // uint32_t FunctionContentLength =0  ; 
    // *this << FunctionContentLength;

    *this << FunctionInfo.AccessLevel;
    *this << FunctionInfo.bIsStatic;
    *this << FunctionInfo.ReturnType;
    *this << FunctionInfo.Name;

    *this << (uint32_t)FunctionInfo.Parameters.Num();
    for(auto & Parameter : FunctionInfo.Parameters)
    {
        *this << Parameter;
    }

    *this << (uint32_t)FunctionInfo.VariableStack.Num();
    for(auto & Variable : FunctionInfo.VariableStack)
    {
        *this << Variable;
    }

    *this<<FunctionInfo.LinePositions;

    *this<<(uint32_t)FunctionInfo.BreakablePoints.Num();
    for(auto & BreakablePoint : FunctionInfo.BreakablePoints)
    {
        *this << BreakablePoint;
    }



    ByteCode->ByteCode.Append(Instructions);

    uint32_t FunctionContentLength = ByteCode->ByteCode.Num() - ContentStart;
    ByteCode->ByteCode[FunctionContentLengthPosition +0] = FunctionContentLength & 0xFF;
    ByteCode->ByteCode[FunctionContentLengthPosition+ 1] = (FunctionContentLength >> 8) & 0xFF;
    ByteCode->ByteCode[FunctionContentLengthPosition+ 2] = (FunctionContentLength >> 16) & 0xFF;
    ByteCode->ByteCode[FunctionContentLengthPosition+ 3] = (FunctionContentLength >> 24) & 0xFF;

}



void CBytecodeWriter::AddField(const CByteCodeFile::SFieldInfo &FieldInfo)
{
    *this << EByteCodeSectionType::Field;
    // uint32_t FieldContentLength = FieldInfo.CountSize();
    // *this << FieldContentLength;
    *this << FieldInfo.AccessLevel;
    *this << FieldInfo.Decorator;
    *this << FieldInfo.Type;
    *this << FieldInfo.Name;
    *this << FieldInfo.FieldIndex;
    *this << FieldInfo.DefaultValue.HasValue();
    if(FieldInfo.DefaultValue.HasValue())
    {
        *this << FieldInfo.DefaultValue.GetValue();
    }
}

void CBytecodeWriter::AddVirtualFunctionTable(const TVector<SFunctionID> &VirtualFunctionTable)
{
    *this << (uint32_t)VirtualFunctionTable.Num();
    for(auto & FunctionID : VirtualFunctionTable)
    {
        *this << FunctionID;
    }
}

void CBytecodeWriter::AddBinocularOperation(EByteCodeInstructions Operator)
{
    *this << Operator;
}

void CBytecodeWriter::AddLoadLiteralOperation(uint8_t InValue)
{
    *this << EByteCodeInstructions::Ldc_u8;
    *this << InValue;
}

void CBytecodeWriter::AddLoadLiteralOperation(uint16_t InValue)
{
    *this << EByteCodeInstructions::Ldc_u16;
    *this << InValue;
}

void CBytecodeWriter::AddLoadLiteralOperation(uint32_t InValue)
{
    *this << EByteCodeInstructions::Ldc_u32;
    *this << InValue;
}

void CBytecodeWriter::AddLoadLiteralOperation(uint64_t InValue)
{
    *this << EByteCodeInstructions::Ldc_u64;
    *this << InValue;
}

void CBytecodeWriter::AddLoadLiteralOperation(int8_t InValue)
{
    *this << EByteCodeInstructions::Ldc_i8;
    *this << InValue;
}

void CBytecodeWriter::AddLoadLiteralOperation(int16_t InValue)
{
    *this << EByteCodeInstructions::Ldc_i16;
    *this << InValue;
}

void CBytecodeWriter::AddLoadLiteralOperation(int32_t InValue)
{
    *this << EByteCodeInstructions::Ldc_i32;
    *this << InValue;
}


void CBytecodeWriter::AddLoadLiteralOperation(int64_t InValue)
{
    switch(InValue)
    {
        case 0:
            *this << EByteCodeInstructions::Ldc_i64_0;
            return;
        case 1:
            *this << EByteCodeInstructions::Ldc_i64_1;
            return;

        case 2: 
            *this << EByteCodeInstructions::Ldc_i64_2;
            return;

        case 3:
            *this << EByteCodeInstructions::Ldc_i64_3;
            return;

        case 4:
            *this << EByteCodeInstructions::Ldc_i64_4;
            return;

        case 5:
            *this << EByteCodeInstructions::Ldc_i64_5;
            return;

        case 6:
            *this << EByteCodeInstructions::Ldc_i64_6;
            return;

        case 7:
            *this << EByteCodeInstructions::Ldc_i64_7;
            return;

        case 8:
            *this << EByteCodeInstructions::Ldc_i64_8;
            return;

        case -1:
            *this << EByteCodeInstructions::Ldc_i64_m1;
            return;

        default:
            {
                *this << EByteCodeInstructions::Ldc_i64;
                *this << InValue;
            }
        break;
    }

}




void CBytecodeWriter::AddLoadLiteralOperation(bool InValue)
{
    if(InValue)
    {
        *this << EByteCodeInstructions::Ldc_True;
    }
    else
    {
        *this << EByteCodeInstructions::Ldc_False;
    }
}

void CBytecodeWriter::AddLoadLiteralOperation(float InValue)
{
    *this << EByteCodeInstructions::Ldc_r32;
    *this << InValue;
}

void CBytecodeWriter::AddLoadLiteralOperation(double InValue)
{
    *this << EByteCodeInstructions::Ldc_r64;
    *this << InValue;
}

void CBytecodeWriter::AddLoadLiteralOperation(const char32_t &InValue)
{
    *this << EByteCodeInstructions::Ldc_Char;
    *this << (uint32_t)InValue;
}

void CBytecodeWriter::AddLoadLiteralOperation(const CString &InValue)
{
    *this << EByteCodeInstructions::Ldstr;
    *this << InValue;
}

void CBytecodeWriter::AddStoreVariableOperation(uint32_t VariableIndex)
{
    *this << EByteCodeInstructions::Stloc;
    *this << VariableIndex;
}

void CBytecodeWriter::AddLoadVariableOperation(uint32_t VariableIndex)
{
    *this << EByteCodeInstructions::Ldloc;
    *this << VariableIndex;
}

void CBytecodeWriter::AddLoadVariableAddressOperation(uint32_t VariableIndex)
{
    *this << EByteCodeInstructions::Ldloca;
    *this << VariableIndex;
}

void CBytecodeWriter::AddFunctionCallOperation(const CByteCodeFile::SFunctionCallInfo & FunctionCallInfo)
{
    *this << EByteCodeInstructions::Call;
    *this << FunctionCallInfo;

}

void CBytecodeWriter::AddVirtualFunctionCallOperation(const CByteCodeFile::SFunctionCallInfo &FunctionCallInfo)
{
    *this << EByteCodeInstructions::CallVirt;
    *this << FunctionCallInfo;
}

void CBytecodeWriter::AddLoadArgumentOperation(uint32_t ParameterIndex)
{
    *this << EByteCodeInstructions::Ldarg;
    *this << ParameterIndex;
}

void CBytecodeWriter::AddLoadArgumentAddressOperation(uint32_t ParameterIndex)
{
    *this << EByteCodeInstructions::Ldarga;
    *this << ParameterIndex;
}

void CBytecodeWriter::AddStoreArgumentOperation(uint32_t ParameterIndex)
{
    *this << EByteCodeInstructions::Starg;
    *this << ParameterIndex;
}

void CBytecodeWriter::AddBoxOperation(const CByteCodeFile::STypeInfo &TypeInfo)
{
    *this << EByteCodeInstructions::Box;
    *this << TypeInfo;
}

void CBytecodeWriter::AddStoreFieldOperation(uint32_t FieldIndex)
{
    assert(FieldIndex!=-1);
    *this << EByteCodeInstructions::Stfld;
    *this << FieldIndex;
}

void CBytecodeWriter::AddStoreStaticFieldOperation(const STypeID& Class, uint32_t FieldIndex)
{
    assert(FieldIndex!=-1);
    *this << EByteCodeInstructions::Stsfld;
    *this << Class;
    *this << FieldIndex;
}

void CBytecodeWriter::AddLoadFieldOperation(uint32_t FieldIndex)
{
    assert(FieldIndex!=-1);
    *this << EByteCodeInstructions::Ldfld;
    *this << FieldIndex;
}

void CBytecodeWriter::AddLoadFieldAddressOperation(uint32_t FieldIndex)
{
    assert(FieldIndex!=-1);
    *this << EByteCodeInstructions::Ldflda;
    *this << FieldIndex;
}

void CBytecodeWriter::AddLoadStaticFieldOperation(const STypeID& Class,  uint32_t FieldIndex)
{
    assert(FieldIndex!=-1);
    *this << EByteCodeInstructions::Ldsfld;
    *this << Class;
    *this << FieldIndex;
}

void CBytecodeWriter::AddLoadStaticFieldAddressOperation(const STypeID& Class, uint32_t FieldIndex)
{
    assert(FieldIndex!=-1);
    *this << EByteCodeInstructions::Ldsflda;
    *this << Class;
    *this << FieldIndex;
}

void CBytecodeWriter::AddNewObjectOperation(const CByteCodeFile::SFunctionCallInfo &ConstructorInfo)
{
    *this << EByteCodeInstructions::New_Object;
    *this << ConstructorInfo;
}

void CBytecodeWriter::AddInitValueTypeOperation(const STypeID& InType)
{
    *this << EByteCodeInstructions::Init_ValueType;
    *this << InType;
}

void CBytecodeWriter::AddReturnOperation()
{
    *this << EByteCodeInstructions::Ret;
}

void CBytecodeWriter::AddDoNothingOperation()
{
    *this << EByteCodeInstructions::Nop;
}

void CBytecodeWriter::AddJumpOperation(uint32_t NewLineIndex, EByteCodeInstructions InInstruction)
{
    *this <<InInstruction;
    *this << NewLineIndex;
}

void CBytecodeWriter::AddInstruction(EByteCodeInstructions Instruction)
{
    *this << Instruction;
}

void CBytecodeWriter::AddInnerTypeConvertion(EScriptInnerType FromType, EScriptInnerType ToType)
{
    switch(FromType)
    {
        case EScriptInnerType::Bool:
            {
                switch(ToType)
                {
                    case EScriptInnerType::Int8:
                        *this << EByteCodeInstructions::Convert_Bool_To_i8;
                        return;
                    case EScriptInnerType::Int16:
                        *this << EByteCodeInstructions::Convert_Bool_To_i16;
                        return;
                    case EScriptInnerType::Int32:
                        *this << EByteCodeInstructions::Convert_Bool_To_i32;
                        return;
                    case EScriptInnerType::Int64:
                        *this << EByteCodeInstructions::Convert_Bool_To_i64;
                        return;
                    case EScriptInnerType::Uint8:
                        *this << EByteCodeInstructions::Convert_Bool_To_u8;
                        return;
                    case EScriptInnerType::Uint16:
                        *this << EByteCodeInstructions::Convert_Bool_To_u16;
                        return;
                    case EScriptInnerType::Uint32:
                        *this << EByteCodeInstructions::Convert_Bool_To_u32;
                        return;
                    case EScriptInnerType::Uint64:
                        *this << EByteCodeInstructions::Convert_Bool_To_u64;
                        return;
                    case EScriptInnerType::Float:
                        *this << EByteCodeInstructions::Convert_Bool_To_r32;
                        return;
                    case EScriptInnerType::Double:
                        *this << EByteCodeInstructions::Convert_Bool_To_r64;
                        return;
                    case EScriptInnerType::Char:
                        *this << EByteCodeInstructions::Convert_Bool_To_Char;
                        return;
                    default:
                        assert(false&&"unimplement");
                        return;
                }
            }
        case EScriptInnerType:: Int8:
            {
                switch(ToType)
                {
                    case EScriptInnerType::Bool:
                        *this << EByteCodeInstructions::Convert_i8_To_Bool;
                        return;
                    case EScriptInnerType::Int16:
                        *this << EByteCodeInstructions::Convert_i8_To_i16;
                        return;
                    case EScriptInnerType::Int32:
                        *this << EByteCodeInstructions::Convert_i8_To_i32;
                        return;
                    case EScriptInnerType::Int64:
                        *this << EByteCodeInstructions::Convert_i8_To_i64;
                        return;
                    case EScriptInnerType::Uint8:
                        *this << EByteCodeInstructions::Convert_i8_To_u8;
                        return;
                    case EScriptInnerType::Uint16:
                        *this << EByteCodeInstructions::Convert_i8_To_u16;
                        return;
                    case EScriptInnerType::Uint32:
                        *this << EByteCodeInstructions::Convert_i8_To_u32;
                        return;
                    case EScriptInnerType::Uint64:
                        *this << EByteCodeInstructions::Convert_i8_To_u64;
                        return;
                    case EScriptInnerType::Float:
                        *this << EByteCodeInstructions::Convert_i8_To_r32;
                        return;
                    case EScriptInnerType::Double:
                        *this << EByteCodeInstructions::Convert_i8_To_r64;
                        return;
                    case EScriptInnerType::Char:
                        *this << EByteCodeInstructions::Convert_i8_To_Char;
                        return;
                    default:
                        assert(false&&"unimplement");
                        return;
                }
            }
        
        case EScriptInnerType::Int16:
            {
                switch(ToType)
                {
                    case EScriptInnerType::Bool:
                        *this << EByteCodeInstructions::Convert_i16_To_Bool;
                        return;
                    case EScriptInnerType::Int8:
                        *this << EByteCodeInstructions::Convert_i16_To_i8;
                        return;
                    case EScriptInnerType::Int32:
                        *this << EByteCodeInstructions::Convert_i16_To_i32;
                        return;
                    case EScriptInnerType::Int64:
                        *this << EByteCodeInstructions::Convert_i16_To_i64;
                        return;
                    case EScriptInnerType::Uint8:
                        *this << EByteCodeInstructions::Convert_i16_To_u8;
                        return;
                    case EScriptInnerType::Uint16:
                        *this << EByteCodeInstructions::Convert_i16_To_u16;
                        return;
                    case EScriptInnerType::Uint32:
                        *this << EByteCodeInstructions::Convert_i16_To_u32;
                        return;
                    case EScriptInnerType::Uint64:
                        *this << EByteCodeInstructions::Convert_i16_To_u64;
                        return;
                    case EScriptInnerType::Float:
                        *this << EByteCodeInstructions::Convert_i16_To_r32;
                        return;
                    case EScriptInnerType::Double:
                        *this << EByteCodeInstructions::Convert_i16_To_r64;
                        return;
                    case EScriptInnerType::Char:
                        *this << EByteCodeInstructions::Convert_i16_To_Char;
                        return;
                    default:
                        assert(false&&"unimplement");
                        return;
                }
            }
        case EScriptInnerType::Int32:
            {
                switch(ToType)
                {
                    case EScriptInnerType::Bool:
                        *this << EByteCodeInstructions::Convert_i32_To_Bool;
                        return;
                    case EScriptInnerType::Int8:
                        *this << EByteCodeInstructions::Convert_i32_To_i8;
                        return;
                    case EScriptInnerType::Int16:
                        *this << EByteCodeInstructions::Convert_i32_To_i16;
                        return;
                    case EScriptInnerType::Int64:
                        *this << EByteCodeInstructions::Convert_i32_To_i64;
                        return;
                    case EScriptInnerType::Uint8:
                        *this << EByteCodeInstructions::Convert_i32_To_u8;
                        return;
                    case EScriptInnerType::Uint16:
                        *this << EByteCodeInstructions::Convert_i32_To_u16;
                        return;
                    case EScriptInnerType::Uint32:
                        *this << EByteCodeInstructions::Convert_i32_To_u32;
                        return;
                    case EScriptInnerType::Uint64:
                        *this << EByteCodeInstructions::Convert_i32_To_u64;
                        return;
                    case EScriptInnerType::Float:
                        *this << EByteCodeInstructions::Convert_i32_To_r32;
                        return;
                    case EScriptInnerType::Double:
                        *this << EByteCodeInstructions::Convert_i32_To_r64;
                        return;
                    case EScriptInnerType::Char:
                        *this << EByteCodeInstructions::Convert_i32_To_Char;
                        return;
                    default:
                        assert(false&&"unimplement");
                        return;
                }
            }
        case EScriptInnerType::Int64:
        {
            switch(ToType)
            {
                case EScriptInnerType::Bool:
                    *this << EByteCodeInstructions::Convert_i64_To_Bool;
                    return;
                case EScriptInnerType::Int8:
                    *this << EByteCodeInstructions::Convert_i64_To_i8;
                    return;
                case EScriptInnerType::Int16:
                    *this << EByteCodeInstructions::Convert_i64_To_i16;
                    return;
                case EScriptInnerType::Int32:
                    *this << EByteCodeInstructions::Convert_i64_To_i32;
                    return;
                case EScriptInnerType::Uint8:
                    *this << EByteCodeInstructions::Convert_i64_To_u8;
                    return;
                case EScriptInnerType::Uint16:
                    *this << EByteCodeInstructions::Convert_i64_To_u16;
                    return;
                case EScriptInnerType::Uint32:
                    *this << EByteCodeInstructions::Convert_i64_To_u32;
                    return;
                case EScriptInnerType::Uint64:
                    *this << EByteCodeInstructions::Convert_i64_To_u64;
                    return;
                case EScriptInnerType::Float:
                    *this << EByteCodeInstructions::Convert_i64_To_r32;
                    return;
                case EScriptInnerType::Double:
                    *this << EByteCodeInstructions::Convert_i64_To_r64;
                    return;
                case EScriptInnerType::Char:
                    *this << EByteCodeInstructions::Convert_i64_To_Char;
                    return;
                default:
                    assert(false&&"unimplement");
                    return;
            }
        }
        case EScriptInnerType::Uint8:
        {
            switch(ToType)
            {
                case EScriptInnerType::Bool:
                    *this << EByteCodeInstructions::Convert_u8_To_Bool;
                    return;
                case EScriptInnerType::Int8:
                    *this << EByteCodeInstructions::Convert_u8_To_i8;
                    return;
                case EScriptInnerType::Int16:
                    *this << EByteCodeInstructions::Convert_u8_To_i16;
                    return;
                case EScriptInnerType::Int32:
                    *this << EByteCodeInstructions::Convert_u8_To_i32;
                    return;
                case EScriptInnerType::Int64:
                    *this << EByteCodeInstructions::Convert_u8_To_i64;
                    return;
                case EScriptInnerType::Uint16:
                    *this << EByteCodeInstructions::Convert_u8_To_u16;
                    return;
                case EScriptInnerType::Uint32:
                    *this << EByteCodeInstructions::Convert_u8_To_u32;
                    return;
                case EScriptInnerType::Uint64:
                    *this << EByteCodeInstructions::Convert_u8_To_u64;
                    return;
                case EScriptInnerType::Float:
                    *this << EByteCodeInstructions::Convert_u8_To_r32;
                    return;
                case EScriptInnerType::Double:
                    *this << EByteCodeInstructions::Convert_u8_To_r64;
                    return;
                case EScriptInnerType::Char:
                    *this << EByteCodeInstructions::Convert_u8_To_Char;
                    return;
                default:
                    assert(false&&"unimplement");
                    return;
            }
        }

        case EScriptInnerType::Uint16:
        {
            switch(ToType)
            {
                case EScriptInnerType::Bool:
                    *this << EByteCodeInstructions::Convert_u16_To_Bool;
                    return;
                case EScriptInnerType::Int8:
                    *this << EByteCodeInstructions::Convert_u16_To_i8;
                    return;
                case EScriptInnerType::Int16:
                    *this << EByteCodeInstructions::Convert_u16_To_i16;
                    return;
                case EScriptInnerType::Int32:
                    *this << EByteCodeInstructions::Convert_u16_To_i32;
                    return;
                case EScriptInnerType::Int64:
                    *this << EByteCodeInstructions::Convert_u16_To_i64;
                    return;
                case EScriptInnerType::Uint8:
                    *this << EByteCodeInstructions::Convert_u16_To_u8;
                    return;
                case EScriptInnerType::Uint32:
                    *this << EByteCodeInstructions::Convert_u16_To_u32;
                    return;
                case EScriptInnerType::Uint64:
                    *this << EByteCodeInstructions::Convert_u16_To_u64;
                    return;
                case EScriptInnerType::Float:
                    *this << EByteCodeInstructions::Convert_u16_To_r32;
                    return;
                case EScriptInnerType::Double:
                    *this << EByteCodeInstructions::Convert_u16_To_r64;
                    return;
                case EScriptInnerType::Char:
                    *this << EByteCodeInstructions::Convert_u16_To_Char;
                    return;
                default:
                    assert(false&&"unimplement");
                    return;
            }
        }

        case EScriptInnerType::Uint32:
        {
            switch(ToType)
            {
                case EScriptInnerType::Bool:
                    *this << EByteCodeInstructions::Convert_u32_To_Bool;
                    return;
                case EScriptInnerType::Int8:
                    *this << EByteCodeInstructions::Convert_u32_To_i8;
                    return;
                case EScriptInnerType::Int16:
                    *this << EByteCodeInstructions::Convert_u32_To_i16;
                    return;
                case EScriptInnerType::Int32:
                    *this << EByteCodeInstructions::Convert_u32_To_i32;
                    return;
                case EScriptInnerType::Int64:
                    *this << EByteCodeInstructions::Convert_u32_To_i64;
                    return;
                case EScriptInnerType::Uint8:
                    *this << EByteCodeInstructions::Convert_u32_To_u8;
                    return;
                case EScriptInnerType::Uint16:
                    *this << EByteCodeInstructions::Convert_u32_To_u16;
                    return;
                case EScriptInnerType::Uint64:
                    *this << EByteCodeInstructions::Convert_u32_To_u64;
                    return;
                case EScriptInnerType::Float:
                    *this << EByteCodeInstructions::Convert_u32_To_r32;
                    return;
                case EScriptInnerType::Double:
                    *this << EByteCodeInstructions::Convert_u32_To_r64;
                    return;
                case EScriptInnerType::Char:
                    *this << EByteCodeInstructions::Convert_u32_To_Char;
                    return;
                default:
                    assert(false&&"unimplement");
                    return;
            }
        }

        case EScriptInnerType::Uint64:
        {
            switch(ToType)
            {
                case EScriptInnerType::Bool:
                    *this << EByteCodeInstructions::Convert_u64_To_Bool;
                    return;
                case EScriptInnerType::Int8:
                    *this << EByteCodeInstructions::Convert_u64_To_i8;
                    return;
                case EScriptInnerType::Int16:
                    *this << EByteCodeInstructions::Convert_u64_To_i16;
                    return;
                case EScriptInnerType::Int32:
                    *this << EByteCodeInstructions::Convert_u64_To_i32;
                    return;
                case EScriptInnerType::Int64:
                    *this << EByteCodeInstructions::Convert_u64_To_i64;
                    return;
                case EScriptInnerType::Uint8:
                    *this << EByteCodeInstructions::Convert_u64_To_u8;
                    return;
                case EScriptInnerType::Uint16:
                    *this << EByteCodeInstructions::Convert_u64_To_u16;
                    return;
                case EScriptInnerType::Uint32:
                    *this << EByteCodeInstructions::Convert_u64_To_u32;
                    return;
                case EScriptInnerType::Float:
                    *this << EByteCodeInstructions::Convert_u64_To_r32;
                    return;
                case EScriptInnerType::Double:
                    *this << EByteCodeInstructions::Convert_u64_To_r64;
                    return;
                case EScriptInnerType::Char:
                    *this << EByteCodeInstructions::Convert_u64_To_Char;
                    return;
                default:
                    assert(false&&"unimplement");
                    return;
            }
        }
        case EScriptInnerType::Float:
        {
            switch(ToType)
            {
                case EScriptInnerType::Int8:
                    *this << EByteCodeInstructions::Convert_r32_To_i8;
                    return;
                case EScriptInnerType::Int16:
                    *this << EByteCodeInstructions::Convert_r32_To_i16;
                    return;
                case EScriptInnerType::Int32:
                    *this << EByteCodeInstructions::Convert_r32_To_i32;
                    return;
                case EScriptInnerType::Int64:
                    *this << EByteCodeInstructions::Convert_r32_To_i64;
                    return;
                case EScriptInnerType::Uint8:
                    *this << EByteCodeInstructions::Convert_r32_To_u8;
                    return;
                case EScriptInnerType::Uint16:
                    *this << EByteCodeInstructions::Convert_r32_To_u16;
                    return;
                case EScriptInnerType::Uint32:
                    *this << EByteCodeInstructions::Convert_r32_To_u32;
                    return;
                case EScriptInnerType::Uint64:
                    *this << EByteCodeInstructions::Convert_r32_To_u64;
                    return;
                case EScriptInnerType::Double:
                    *this << EByteCodeInstructions::Convert_r32_To_r64;
                    return;
                default:
                    assert(false&&"unimplement");
                    return;
            }
        }

        case EScriptInnerType::Double:
        {
            switch(ToType)
            {
                case EScriptInnerType::Int8:
                    *this << EByteCodeInstructions::Convert_r64_To_i8;
                    return;
                case EScriptInnerType::Int16:
                    *this << EByteCodeInstructions::Convert_r64_To_i16;
                    return;
                case EScriptInnerType::Int32:
                    *this << EByteCodeInstructions::Convert_r64_To_i32;
                    return;
                case EScriptInnerType::Int64:
                    *this << EByteCodeInstructions::Convert_r64_To_i64;
                    return;
                case EScriptInnerType::Uint8:
                    *this << EByteCodeInstructions::Convert_r64_To_u8;
                    return;
                case EScriptInnerType::Uint16:
                    *this << EByteCodeInstructions::Convert_r64_To_u16;
                    return;
                case EScriptInnerType::Uint32:
                    *this << EByteCodeInstructions::Convert_r64_To_u32;
                    return;
                case EScriptInnerType::Uint64:
                    *this << EByteCodeInstructions::Convert_r64_To_u64;
                    return;
                case EScriptInnerType::Float:
                    *this << EByteCodeInstructions::Convert_r64_To_r32;
                    return;
                case EScriptInnerType::Char:
                    *this << EByteCodeInstructions::Convert_r64_To_Char;
                    return;
                default:
                    assert(false&&"unimplement");
                    return;
            }
        }

        case EScriptInnerType::Char:
        {
            switch(ToType)
            {
                case EScriptInnerType::Int8:
                    *this << EByteCodeInstructions::Convert_Char_To_i8;
                    return;
                case EScriptInnerType::Int16:
                    *this << EByteCodeInstructions::Convert_Char_To_i16;
                    return;
                case EScriptInnerType::Int32:
                    *this << EByteCodeInstructions::Convert_Char_To_i32;
                    return;
                case EScriptInnerType::Int64:
                    *this << EByteCodeInstructions::Convert_Char_To_i64;
                    return;
                case EScriptInnerType::Uint8:
                    *this << EByteCodeInstructions::Convert_Char_To_u8;
                    return;
                case EScriptInnerType::Uint16:
                    *this << EByteCodeInstructions::Convert_Char_To_u16;
                    return;
                case EScriptInnerType::Uint32:
                    *this << EByteCodeInstructions::Convert_Char_To_u32;
                    return;
                case EScriptInnerType::Uint64:
                    *this << EByteCodeInstructions::Convert_Char_To_u64;
                    return;
                case EScriptInnerType::Float:
                    *this << EByteCodeInstructions::Convert_Char_To_r32;
                    return;
                case EScriptInnerType::Double:
                    *this << EByteCodeInstructions::Convert_Char_To_r64;
                    return;
                default:
                    assert(false&&"unimplement");
                    return;
            }
        }

        default:
            assert(false&&"unimplement");
            return;
    }
}

void CBytecodeWriter::AddLoadFunctionOperation(const CByteCodeFile::SFunctionLoadInfo &FunctionLoadInfo)
{
    *this<<EByteCodeInstructions::LoadFunction;
    *this<<FunctionLoadInfo;
}

void CBytecodeWriter::AddCastClassOperation(const STypeID &Class)
{
    *this<<EByteCodeInstructions::CastClass;
    *this<<Class;
}
